import datetime
from google.appengine.ext import db
from google.appengine.api import users
from urlparse import urljoin
from django.utils import simplejson
import logging
import os

class Project(db.Model):
    name = db.StringProperty(required=True)
    owner = db.UserProperty()
    secret_key = db.StringProperty(required=True)
    moddate = db.DateTimeProperty()
    test_path = db.StringProperty(required=True)
    source_path = db.StringProperty(required=True)
    repository_path = db.StringProperty(required=False)
    hudson_ping_url = db.StringProperty(required=False)
    
    def latest_revision(self):
        return db.GqlQuery("SELECT * FROM Revision WHERE project = :1 ORDER BY timestamp DESC", 
            self).get()
    
    def url(self):
        return "http://code.google.com/p/%s/" % (self.name, )
        
class Revision(db.Model):
    # info from http://code.google.com/p/support/wiki/PostCommitWebHooks
    name = db.StringProperty(required=True)
    project = db.ReferenceProperty(reference_class=Project, required=True)
    url = db.LinkProperty(required=True)
    author = db.StringProperty(required=True)
    timestamp = db.DateTimeProperty(required=True)
    message = db.TextProperty(required=True)
    added = db.StringListProperty(required=True)
    modified = db.StringListProperty(required=True)
    removed = db.StringListProperty(required=True)
    # additional info added by app
    # added from output of closure compiler
    warnings = db.StringListProperty()
    errors = db.StringListProperty()
    bytes = db.IntegerProperty(required=False)
    bytes_gzip = db.IntegerProperty(required=False)
    bytes_compressed = db.IntegerProperty(required=False)
    # added after doing at least one test run
    tests = db.IntegerProperty(required=False)
    failures = db.IntegerProperty(required=False)
    compiled = db.BooleanProperty(required=True, default=False)
    runner_error = db.BooleanProperty(required=False, default=False)
    
    def url_pretty(self):
        return "http://code.google.com/p/%s/source/detail?r=%s" % (self.project.name, self.name)
    
    def author_html(self):
        return self.author.replace('<', '&lt;').replace('>', '&gt;')
        
    def formatted_time(self):
        return self.timestamp.strftime("%A, %d. %B %Y %I:%M%p")
        
    def compile(self):
        project = self.project
        source = urljoin(self.url, project.source_path) + '?r=' + self.name
        import httplib, urllib, sys
        params = urllib.urlencode([
            ('code_url', source), # <--- This parameter has a new name!
            ('compilation_level', 'SIMPLE_OPTIMIZATIONS'),
            ('output_format', 'json'),
            ('output_info', 'warnings'),
            ('output_info', 'errors'),
            ('output_info', 'statistics'),
            ('warning_level', 'verbose'),
        ])
        headers = { "Content-type": "application/x-www-form-urlencoded" }
        conn = httplib.HTTPConnection('closure-compiler.appspot.com')
        conn.request('POST', '/compile', params, headers)
        response = conn.getresponse()
        data = simplejson.loads(response.read())
        if 'warnings' in data.keys():
            warnings = []
            for warning in data['warnings']:
                warnings.append(warning['warning'])
            self.warnings = warnings
        if 'errors' in data.keys():
            errors = []
            for error in data['errors']:
                errors.append(error['error'])
            self.errors = errors
        self.bytes = data['statistics']['originalSize']
        self.bytes_gzip = data['statistics']['originalGzipSize']
        self.bytes_compressed = data['statistics']['compressedGzipSize']
        self.compiled = True
        self.put()
        return data
    
    def passing(self):
        if self.tests:
            if self.failures:
                return self.tests - self.failures
            else:
                return self.tests
        else:
            return None
    
    def test_results(self):
        return TestRun.all().filter('revision =', self)
        
    def add_testrun(self, total, browsername, failures, supported, error):
        self.tests = total
        query = TestRun.all().filter('revision = ', self).filter(
            'browsername = ', browsername)
        for old in query:
            old.delete()
        run = TestRun(project=self.project, revision=self, 
            browsername=browsername, failures=failures, total=total, 
            supported=supported, error=error)
        if error:
            self.runner_error = True
        browser = Browser.all().filter('name =', browsername).get()
        if self.key() in browser.queue:
            browser.queue.remove(self.key())
        browser.put()
        run.put()
        if not self.failures or len(failures) > self.failures:
            if supported:
                self.failures = len(failures)
        self.put()
    
    def test_url(self):
        return "%s%s?r=%s" % (self.project.repository_path, self.project.test_path, self.name)
    
    def result_html(self):
        if self.tests == None or self.tests == 0:
            klass = 'untested'
        elif self.failures > 0 or self.runner_error:
            klass = 'failures'
        else:
            klass = ''
        
        
        if self.runner_error or (self.compiled and len(self.errors) > 0):
            passed = 'errors'
        elif self.tests == None or self.tests == 0:
            passed = 'untested'
        else:
            passed = '%s/%s test cases passed' % (self.passing(), self.tests)

        return '<span class="results %s">%s</span>' % (klass, passed)
        
class TestRun(db.Model):
    project = db.ReferenceProperty(reference_class=Project, required=True)
    revision = db.ReferenceProperty(reference_class=Revision, required=True)
    browsername = db.StringProperty(required=True)
    failures = db.StringListProperty()
    total = db.IntegerProperty(required=False)
    supported = db.BooleanProperty(required=True, default=True)
    error = db.BooleanProperty(required=False)


class Browser(db.Model):
    name = db.StringProperty(required=True)
    queue = db.ListProperty(db.Key)
    last_checkin = db.DateTimeProperty(required=False)
    
    def add_to_queue(self, revision):
        self.queue.insert(0, revision.key())
        self.put()
    
    def next_revision(self):
        if len(self.queue) > 0:
            return Revision.get(self.queue[0])
        else:
            return None